/*
 * Change Logs:
 * Date           Author       Notes
 * 2022-03-27     chenbin
 */
#include "fstorage.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "fstorage"
#define DBG_LEVEL DBG_INFO
#include <rtdbg.h>

#include "fal.h"

fstorage_interface_t * fstorage_fal_open(const char* fal_part_name, uint32_t openflags)
{
	fstorage_interface_t *interface = NULL;
	const struct fal_partition *fal_part = NULL;
	
	fal_part = fal_partition_find(fal_part_name);
	if(fal_part == NULL)
	{
		goto __exit_fail;
	}
	interface = rt_calloc(1, sizeof(fstorage_interface_t));
	if(interface == NULL)
	{
		goto __exit_fail;
	}
	interface->type = 1;
	interface->read_pos = 0;
	interface->write_pos = 0;
	interface->file_fd = -1;
	
	interface->data_max = fal_part->len;
	interface->openflags = openflags;
	interface->fal_part = (void*)fal_part;
	rt_memset(interface->name, 0, sizeof(interface->name));
	rt_memcpy(interface->name, fal_part_name, rt_strnlen(fal_part_name, sizeof(interface->name)) );
	
	return interface;
	
__exit_fail:
	if(interface != NULL)
	{
		rt_free(interface);
	}
	return NULL;
}

void fstorage_fal_close(fstorage_interface_t * interface)
{
	if(interface != NULL)
	{
		rt_free(interface);
	}
	return;
}

#if (FBOOT_USING_FILESYSTEM == 1)

#ifdef RT_USING_DFS
#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
#endif

#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/time.h>
#include <sys/ioctl.h>

static int file_is_exist(const char *filepath)
{
	struct stat filestat = {0};
	if (filepath != NULL)
	{
		if ((stat(filepath, &filestat) >= 0) && (!S_ISDIR(filestat.st_mode)))
		{
			return 1;
		}
	}
	return 0;
}

fstorage_interface_t * fstorage_fs_open(const char* fs_file_path, uint32_t openflags)
{
	fstorage_interface_t *interface = NULL;
	int file_fd = -1;
	int file_flags = 0;
	
	if( (openflags & FSTORAGE_CHECK_EXIST) == FSTORAGE_CHECK_EXIST )
	{
		if(file_is_exist(fs_file_path) == 0)
		{
			goto __exit_fail;
		}
	}
	
	if( (openflags & FSTORAGE_READ) == FSTORAGE_READ )
	{
		file_flags = O_RDONLY;
	}
	if( (openflags & FSTORAGE_WRITE) == FSTORAGE_WRITE )
	{
		file_flags = O_WRONLY;
	}
	if( (openflags & FSTORAGE_READWRITE) == FSTORAGE_READWRITE )
	{
		file_flags = O_RDWR;
	}
	
	if( (openflags & FSTORAGE_CREATE_NEW) == FSTORAGE_CREATE_NEW )
	{
		file_flags |= (O_CREAT);  // create
		file_flags |= (O_TRUNC);  // clear
	}
	
	file_fd = open(fs_file_path, file_flags, 0);
	if (file_fd < 0)
	{
		goto __exit_fail;
	}
	interface = rt_calloc(1, sizeof(fstorage_interface_t));
	if(interface == NULL)
	{
		goto __exit_fail;
	}
	interface->file_fd = file_fd;
	interface->data_max = lseek(interface->file_fd, 0L, SEEK_END);
	lseek(interface->file_fd, 0, SEEK_SET);
	
	interface->type = 2;
	interface->read_pos = 0;
	interface->write_pos = 0;
	interface->fal_part = NULL;
	
	interface->openflags = openflags;
	rt_memset(interface->name, 0, sizeof(interface->name));
	rt_memcpy(interface->name, fs_file_path, rt_strnlen(fs_file_path, sizeof(interface->name)) );
	
	return interface;
	
__exit_fail:
	if(interface != NULL)
	{
		rt_free(interface);
	}	
	if (file_fd >= 0)
	{
		close(file_fd);
	}
	return NULL;
}

void fstorage_fs_close(fstorage_interface_t * interface)
{
	if(interface != NULL)
	{
		if(interface->file_fd >= 0)
		{
			close(interface->file_fd);
			interface->file_fd = -1;
		}
		rt_free(interface);
	}
	return;
}

#endif /* FBOOT_USING_FILESYSTEM */


size_t fstorage_read(fstorage_interface_t *interface, size_t offset, void* buffer, size_t len)
{
	int ret = 0;
	if(interface->fal_part != NULL)
	{
		ret = fal_partition_read(interface->fal_part, offset, buffer, len);
	}
#if (FBOOT_USING_FILESYSTEM == 1)
	else
	{
		lseek(interface->file_fd, offset, SEEK_SET);
		ret = read(interface->file_fd, buffer, len);
	}
#endif
	if(ret <= 0)
	{
		LOG_E("read faild: %d = %s(0x%08X, %d, 0x%08X, %d).", ret, __FUNCTION__, interface, offset, buffer, len);
	}
	return ret;
}

size_t fstorage_write(fstorage_interface_t *interface, size_t offset, const void* buffer, size_t len)
{
	int ret = 0;
	if(interface->fal_part != NULL)
	{
		ret = fal_partition_write(interface->fal_part, offset, buffer, len);
	}
#if (FBOOT_USING_FILESYSTEM == 1)
	else
	{
		lseek(interface->file_fd, offset, SEEK_SET);
		ret = write(interface->file_fd, buffer, len);
	}
#endif
	if(ret < 0)
	{
		LOG_E("write faild: %d = %s(0x%08X, %d, 0x%08X, %d).", ret, __FUNCTION__, interface, offset, buffer, len);
	}
	return ret;
}

size_t fstorage_erase(fstorage_interface_t *interface, size_t offset, size_t len)
{
	int ret = 0;
	if(interface->fal_part != NULL)
	{
		ret = fal_partition_erase(interface->fal_part, offset, len);
	}	
	if (ret < 0)
	{
		LOG_E("erase faild: %d = %s(0x%08X, %d, %d).", ret, __FUNCTION__, interface, offset, len);
	}
	return ret;
}

size_t fstorage_size(fstorage_interface_t *interface)
{
	return interface->data_max;
}

